Completed
Push — master ( 706e2b...22ecc8 )
by Sander
42s
created

background.js ➔ ... ➔ API.tabs.then   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
8
9
    API.runtime.onConnect.addListener(function (port) {
10
11
        port.onMessage.addListener(function (msg) {
12
            if (msg === 'credential_amount') {
13
                port.postMessage('credential_amount:' + local_credentials.length);
14
            }
15
16
        });
17
18
    });
19
20
    var master_password = null;
21
22
    function getMasterPasswordSet() {
23
        return (master_password !== null);
24
    }
25
26
    _self.getMasterPasswordSet = getMasterPasswordSet;
27
28
    function setMasterPassword(opts) {
29
        master_password = opts.password;
30
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
31
            // Save the password in plain text on user request.
32
            // No secure local storage is available :/
33
            storage.set('master_password', opts.password);
34
        } else {
35
            storage.set('master_password', null);
36
        }
37
38
        if (opts.password) {
39
            getSettings();
40
        } else {
41
            displayLogoutIcons();
42
        }
43
44
    }
45
46
    _self.setMasterPassword = setMasterPassword;
47
48
49
    var testMasterPasswordAgainst;
50
51
    function isMasterPasswordValid(password) {
52
        try {
53
            PAPI.decryptString(testMasterPasswordAgainst, password);
54
            return true;
55
        } catch (e) {
56
            return false;
57
        }
58
    }
59
60
    _self.isMasterPasswordValid = isMasterPasswordValid;
61
62
63
    var local_credentials = [];
64
    var local_vault = [];
65
    var encryptedFieldSettings = ['accounts'];
66
    _self.settings = {};
67
    _self.ticker = null;
68
    _self.running = false;
69
    function getSettings() {
70
71
        storage.get('settings').then(function (_settings) {
72
            if ((!_settings || Object.keys(_settings).length === 0 || !_settings.hasOwnProperty('accounts')) && !master_password) {
73
                return;
74
            }
75
76
            if (!master_password && _settings.hasOwnProperty('accounts') && _settings.accounts.length > 0) {
77
                _self.settings.isInstalled = 1;
78
                testMasterPasswordAgainst = _settings.accounts;
79
                return;
80
            }
81
82
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
83
                var field = encryptedFieldSettings[i];
84
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
85
            }
86
87
            _self.settings = _settings;
88
89
            if (!_self.settings.hasOwnProperty('ignored_sites')) {
90
                _self.settings.ignored_sites = [];
91
            }
92
93
            if (!_self.settings.hasOwnProperty('disable_browser_autofill')) {
94
                _self.settings.disable_browser_autofill = true;
95
            }
96
            if (!_self.settings.hasOwnProperty('no_results_found_tab')) {
97
                _self.settings.no_results_found_tab = 'list';
98
            }
99
            getCredentials();
100
            if (_self.running) {
101
                clearInterval(_self.ticker);
102
            }
103
            _self.running = true;
104
            _self.ticker = setInterval(function () {
105
106
            }, _self.settings.refreshTime * 1000);
107
108
        });
109
    }
110
111
    _self.getSettings = getSettings;
112
113
    function getRuntimeSettings() {
114
        return _self.settings;
115
    }
116
117
    _self.getRuntimeSettings = getRuntimeSettings;
118
119
    function getSetting(name) {
120
        return _self.settings[name];
121
    }
122
123
    _self.getSetting = getSetting;
124
125
    function saveSettings(settings, cb) {
0 ignored issues
show
Unused Code introduced by
The parameter cb is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
126
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
127
            var field = encryptedFieldSettings[i];
128
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
129
        }
130
131
        if (!settings.hasOwnProperty('ignored_sites')) {
132
            settings.ignored_sites = [];
133
        }
134
135
        if (!settings.hasOwnProperty('disable_browser_autofill')) {
136
            settings.disable_browser_autofill = true;
137
        }
138
139
        if (!_self.settings.hasOwnProperty('password_picker_first_tab')) {
140
            _self.settings.disable_browser_autofill = 'list';
141
        }
142
143
        //window.settings contains the run-time settings
144
        _self.settings = settings;
145
146
147
        storage.set('settings', settings).then(function () {
148
            getSettings();
149
        });
150
151
    }
152
153
    _self.saveSettings = saveSettings;
154
155
156
    function getCredentials() {
157
        if (!master_password) {
158
            return;
159
        }
160
        //console.log('Loading vault with the following settings: ', settings);
161
        var tmpList = [];
162
163
        for (var i = 0; i < _self.settings.accounts.length; i++) {
164
            var account = _self.settings.accounts[i];
165
            /* jshint ignore:start */
166
            (function (inner_account) {
167
                PAPI.getVault(inner_account, function (vault) {
168
                    if (vault.hasOwnProperty('error')) {
169
                        return;
170
                    }
171
                    var _credentials = vault.credentials;
172
                    for (var i = 0; i < _credentials.length; i++) {
173
                        var key = inner_account.vault_password;
174
                        var credential = _credentials[i];
175
                        if (credential.hidden === 1) {
176
                            continue;
177
                        }
178
                        var usedKey = key;
179
                        //Shared credentials are not implemented yet
180
                        if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
181
                            usedKey = PAPI.decryptString(credential.shared_key, key);
182
183
                        }
184
                        credential = PAPI.decryptCredential(credential, usedKey);
185
                        credential.account = inner_account;
186
                        if (credential.delete_time === 0) {
187
                            tmpList.push(credential);
188
                        }
189
190
                    }
191
                    delete vault.credentials;
192
                    local_vault = vault;
193
                    local_credentials = tmpList;
194
195
                    getSharedCredentials(inner_account);
196
197
198
                });
199
            }(account));
200
            /* jshint ignore:end */
201
        }
202
    }
203
204
    _self.getCredentials = getCredentials;
205
206
    function getSharedCredentials(account) {
207
        PAPI.getCredendialsSharedWithUs(account, account.vault.guid, function (credentials) {
208
            for (var i = 0; i < credentials.length; i++) {
209
                var _shared_credential = credentials[i];
210
                var _shared_credential_data;
211
                var sharedKey = PAPI.decryptString(_shared_credential.shared_key, account.vault_password);
212
                try {
213
                    _shared_credential_data = PAPI.decryptSharedCredential(_shared_credential.credential_data, sharedKey);
214
                } catch (e) {
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
215
216
                }
217
                if (_shared_credential_data) {
218
                    delete _shared_credential.credential_data;
219
                    _shared_credential_data.acl = _shared_credential;
220
                    _shared_credential_data.acl.permissions = new SharingACL(_shared_credential_data.acl.permissions);
0 ignored issues
show
Bug introduced by
The variable SharingACL seems to be never declared. If this is a global, consider adding a /** global: SharingACL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
221
                    _shared_credential_data.tags_raw = _shared_credential_data.tags;
222
                    _shared_credential_data.account = account;
223
                    local_credentials.push(_shared_credential_data);
224
                }
225
            }
226
            updateTabsIcon();
227
        });
228
    }
229
230
231
    function getCredentialsByUrl(_url, sender) {
0 ignored issues
show
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
232
        if (!master_password) {
233
            return [];
234
        }
235
        if (!_url || _url === '') {
236
            return [];
237
        }
238
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
239
        var found_list = [];
240
        for (var i = 0; i < local_credentials.length; i++) {
241
            if (local_credentials[i].url && local_credentials[i].username && local_credentials[i].password) {
242
                if (local_credentials[i].url.indexOf(url) !== -1) {
243
                    found_list.push(local_credentials[i]);
244
                }
245
            }
246
        }
247
        return found_list;
248
    }
249
250
    _self.getCredentialsByUrl = getCredentialsByUrl;
251
252
253
    function saveCredential(credential) {
254
        //@TODO save shared password
255
        if (!credential.credential_id) {
256
            PAPI.createCredential(credential.account, credential, credential.account.vault_password, function (createdCredential) {
257
                local_credentials.push(createdCredential);
258
            });
259
        } else {
260
            var credential_index;
261
            for (var i = 0; i < local_credentials.length; i++) {
262
                if (local_credentials[i].guid === credential.guid) {
263
                    credential_index = i;
264
                    break;
265
                }
266
            }
267
268
            if (credential.hasOwnProperty('acl')) {
269
                var permissons = new SharingACL(credential.acl.permissions.permission);
0 ignored issues
show
Bug introduced by
The variable SharingACL seems to be never declared. If this is a global, consider adding a /** global: SharingACL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
270
                if (!permissons.hasPermission(0x02)) {
271
                    return;
272
                }
273
            }
274
275
            PAPI.updateCredential(credential.account, credential, credential.account.vault_password, function (updatedCredential) {
276
                if (credential_index) {
277
                    local_credentials[credential_index] = updatedCredential;
278
                }
279
            });
280
        }
281
    }
282
283
    _self.saveCredential = saveCredential;
284
285
    function getCredentialByGuid(guid) {
286
        for (var i = 0; i < local_credentials.length; i++) {
287
            var credential = local_credentials[i];
288
            if (credential.guid === guid) {
289
                return credential;
290
            }
291
        }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
292
    }
293
294
    _self.getCredentialByGuid = getCredentialByGuid;
295
296
    function getCredentialForHTTPAuth(req) {
297
        return getCredentialsByUrl(req.url)[0];
298
    }
299
300
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
301
302
    var mined_data = [];
303
304
    function minedForm(data, sender) {
305
        var url = sender.url;
306
        var existingLogins = getCredentialsByUrl(sender.url);
307
        var title = API.i18n.getMessage('detected_new_login') + ':';
308
        var minedMatchingID = null;
309
        for (var j = 0; j < existingLogins.length; j++) {
310
            var login = existingLogins[j];
311
            if (login.username === data.username) {
312
                if (login.password !== data.password) {
313
                    minedMatchingID = login.guid;
314
                    title = API.i18n.getMessage('detected_changed_login') + ':';
315
                }
316
                else {
317
                    //console.log('No changes detected');
318
                    delete mined_data[sender.tab.id];
319
                    return;
320
                }
321
            }
322
        }
323
        mined_data[sender.tab.id] = {
324
            title: title,
325
            url: url,
326
            username: data.username,
327
            password: data.password,
328
            label: sender.title,
329
            guid: minedMatchingID
330
        };
331
332
        //console.log('Done mining, ', mined_data, sender.tab.id);
333
    }
334
335
    _self.minedForm = minedForm;
336
337
    function getMinedData(args, sender) {
338
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
339
        var senderUrl = sender.tab.url;
340
        var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
341
        if (!_self.settings) {
342
            return null;
343
        }
344
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
345
            return mined_data[sender.tab.id];
346
        }
347
        var matches = _self.settings.ignored_sites.filter(function (item) {
348
            return typeof item === 'string' && site.indexOf(item) > -1;
349
        });
350
351
        if (matches.length !== 0) {
352
            return null;
353
        }
354
        return mined_data[sender.tab.id];
355
    }
356
357
    _self.getMinedData = getMinedData;
358
359
    function clearMined(args, sender) {
360
        delete mined_data[sender.tab.id];
361
    }
362
363
    _self.clearMined = clearMined;
364
365
    function saveMinedCallback(args) {
366
        createIconForTab(args.sender.tab);
367
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
368
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
369
            });
370
        });
371
    }
372
373
    function ignoreSite(_url) {
374
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
375
            _self.settings.ignored_sites = [];
376
        }
377
        var site = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
378
        if (_self.settings.ignored_sites.indexOf(site) === -1) {
379
            _self.settings.ignored_sites.push(site);
380
            saveSettings(_self.settings);
381
        }
382
    }
383
384
    _self.ignoreSite = ignoreSite;
385
386
    function passToParent(args, sender) {
387
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
388
        });
389
    }
390
391
    _self.passToParent = passToParent;
392
393
    function getActiveTab(opt) {
394
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
395
            var tab = tabs[0];
396
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
397
            });
398
        });
399
    }
400
401
    _self.getActiveTab = getActiveTab;
402
403
    function updateCredentialUrlDoorhanger(login) {
404
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
405
            var tab = tabs[0];
406
            var data = login;
407
            data.url = tab.url;
408
            data.title = API.i18n.getMessage('detected_changed_url') + ':';
409
            API.tabs.sendMessage(tab.id, {
410
                method: 'showUrlUpdateDoorhanger',
411
                args: {data: data}
412
            });
413
        });
414
    }
415
416
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
417
418
    function updateCredentialUrl(data, sender) {
419
        mined_data[sender.tab.id] = data;
420
        saveMined({}, sender);
421
422
    }
423
424
    _self.updateCredentialUrl = updateCredentialUrl;
425
426
    function saveMined(args, sender) {
427
        var data = mined_data[sender.tab.id];
428
        var credential = {},
429
            credential_index;
430
431
        if (data.guid === null) {
432
            credential = PAPI.newCredential();
433
        } else {
434
            for (var i = 0; i < local_credentials.length; i++) {
435
                if (local_credentials[i].guid === data.guid) {
436
                    credential = local_credentials[i];
437
                    credential_index = i;
438
                    break;
439
                }
440
            }
441
        }
442
        if (!credential.hasOwnProperty('account')) {
443
            credential.account = args.account;
444
        }
445
        credential.username = data.username;
446
        credential.password = data.password;
447
        credential.url = sender.tab.url;
448
        if (credential.guid !== null) {
449
            PAPI.updateCredential(credential.account, credential,  credential.account.vault_password, function (updatedCredential) {
450
                updatedCredential.account = credential.account;
451
                if (credential_index) {
452
                    local_credentials[credential_index] = updatedCredential;
453
                }
454
                saveMinedCallback({credential: credential, updated: true, sender: sender});
455
                delete mined_data[sender.tab.id];
456
            });
457
        } else {
458
            credential.label = sender.tab.title;
459
            credential.vault_id =  credential.account.vault.vault_id;
460
            PAPI.createCredential(credential.account, credential, credential.account.vault_password, function (createdCredential) {
461
                createdCredential.account = credential.account;
462
                saveMinedCallback({credential: credential, updated: false, sender: sender});
463
                local_credentials.push(createdCredential);
464
                delete mined_data[sender.tab.id];
465
            });
466
        }
467
    }
468
469
    _self.saveMined = saveMined;
470
471
    function searchCredential(searchText) {
472
        searchText = searchText.toLowerCase();
473
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
474
        var results = [];
475
        for (var i = 0; i < local_credentials.length; i++) {
476
            var credential = local_credentials[i];
477
            for (var f = 0; f < searchFields.length; f++) {
478
                var field = searchFields[f];
479
                if (!credential[field]) {
480
                    continue;
481
                }
482
483
                var field_value = credential[field].toLowerCase();
484
                if (field_value.indexOf(searchText) !== -1) {
485
                    results.push(credential);
486
                    break;
487
                }
488
            }
489
        }
490
        return results;
491
    }
492
493
    _self.searchCredential = searchCredential;
494
495
496
    function injectCreateCredential(args, sender) {
497
        var credential = PAPI.newCredential();
498
        credential.label = args.label;
499
        credential.username = args.username;
500
        credential.password = args.password;
501
        credential.vault_id = local_vault.vault_id;
502
        credential.url = sender.tab.url;
503
        PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
504
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
505
            local_credentials.push(createdCredential);
506
507
        });
508
    }
509
510
    self.injectCreateCredential = injectCreateCredential;
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
511
512
    function isVaultKeySet() {
513
        return (_self.settings.vault_password !== null);
514
    }
515
516
    _self.isVaultKeySet = isVaultKeySet;
517
518
    function isAutoFillEnabled() {
519
        if (!_self.settings.hasOwnProperty('disableAutoFill')) {
520
            return true;
521
        }
522
        return (_self.settings.disableAutoFill === false);
523
    }
524
525
    _self.isAutoFillEnabled = isAutoFillEnabled;
526
527
    var doorhangerData = null;
528
529
    function setDoorhangerData(data) {
530
        doorhangerData = data;
531
    }
532
533
    _self.setDoorhangerData = setDoorhangerData;
534
535
    function getDoorhangerData() {
536
        return doorhangerData;
537
    }
538
539
    _self.getDoorhangerData = getDoorhangerData;
540
541
    function closeSetupTab() {
542
        API.tabs.query({url: 'chrome-extension://' + API.runtime.id + '/html/browser_action/browser_action.html'}).then(function (tabs) {
543
            if (tabs) {
544
                API.tabs.remove(tabs[0].id);
545
            }
546
        });
547
    }
548
549
    _self.closeSetupTab = closeSetupTab;
550
551
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
552
553
        if (!msg || !msg.hasOwnProperty('method')) {
554
            return;
555
        }
556
        var result = false;
557
        if (_self[msg.method]) {
558
            result = _self[msg.method](msg.args, sender);
559
        } else {
560
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
561
        }
562
563
        sendResponse(result);
564
    });
565
566
    var defaultColor = '#0082c9';
567
568
    function createIconForTab(tab) {
569
        if (!master_password) {
570
            return;
571
        }
572
        var tabUrl = tab.url;
573
        var logins = getCredentialsByUrl(tabUrl);
574
        if (tab.active) {
575
            window.contextMenu.setContextItems(logins);
576
        }
577
        var credentialAmount = logins.length;
578
        API.browserAction.setBadgeText({
579
            text: credentialAmount.toString(),
580
            tabId: tab.id
581
        });
582
        API.browserAction.setBadgeBackgroundColor({
583
            color: defaultColor,
584
            tabId: tab.id
585
        });
586
587
        var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
588
        API.browserAction.setTitle({
589
            title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
590
            tabId: tab.id
591
        });
592
    }
593
594
    function displayLogoutIcons() {
595
        if (_self.settings) {
596
            API.tabs.query({}).then(function (tabs) {
597
                for (var t = 0; t < tabs.length; t++) {
598
                    var tab = tabs[t];
599
                    API.browserAction.setBadgeText({
600
                        text: '🔑',
601
                        tabId: tab.id
602
                    });
603
                    API.browserAction.setBadgeBackgroundColor({
604
                        color: '#ff0000',
605
                        tabId: tab.id
606
                    });
607
                    API.browserAction.setTitle({
608
                        title: API.i18n.getMessage('browser_action_title_locked'),
609
                        tabId: tab.id
610
                    });
611
                }
612
            });
613
        }
614
    }
615
616
    function updateTabsIcon() {
617
        API.tabs.query({}).then(function (tabs) {
618
            for (var t = 0; t < tabs.length; t++) {
619
                var tab = tabs[t];
620
                createIconForTab(tab);
621
            }
622
        });
623
    }
624
625
626
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
627
        if (master_password) {
628
            createIconForTab(tab);
629
        } else {
630
            displayLogoutIcons();
631
        }
632
    });
633
634
    API.tabs.onActivated.addListener(function () {
635
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
636
            if (master_password) {
637
                createIconForTab(tabs[0]);
638
            } else {
639
                displayLogoutIcons();
640
            }
641
        });
642
    });
643
644
    displayLogoutIcons();
645
646
647
    storage.get('master_password').then(function (password) {
648
        if (password) {
649
            master_password = password;
650
            API.api.browserAction.setBadgeBackgroundColor({
651
                color: defaultColor
652
            });
653
        }
654
        getSettings();
655
    }).error(function (error) {
656
        if (error === "Data not found") {
657
            getSettings();
658
        }
659
    });
660
    return _window;
661
}());
662
663